﻿using System;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reflection;
using System.Xml;
using System.Linq;

namespace PickGold.Data.Linq
{
	public class MappingFunction : MetaFunction
	{
		private MetaSource _Source;
		private MappingModel _Model;
		private MethodInfo _Method;
		private XmlNode _Config;
		private FunctionAttribute _Function;
		private List<MappingParameter> _Parameters;
		private List<MappingType> _Results;

		public MappingFunction(MetaSource source, MappingModel model, MethodInfo method)
		{
			var cs = method.GetCustomAttributes(typeof(FunctionAttribute), true);
			if (cs != null && cs.Length > 0)
				this._Function = cs[0] as FunctionAttribute;
			this._Config = model.GetNode(method);
			this._Source = source;
			this._Model = model;
			this._Method = method;
		}

		public override bool HasMultipleResults
		{
			get
			{
				if (this._Method.ReturnType == typeof(IMultipleResults) || this._Method.ReturnType.IsSubclassOf(typeof(IMultipleResults)))
					return true;

				return false;
			}
		}

		public override bool IsComposable// false:procedure, true:function
		{
			get
			{
				const bool PROCEDURE = false;
				const bool FUNCTION = true;
				if (this._Config == null)
				{
					if (this._Function == null || string.IsNullOrWhiteSpace(this._Function.Name))
					{
						var t = this._Method.ReturnType;
						var d = t.IsGenericType ? t.GetGenericTypeDefinition() : null;
						if (d == typeof(IQueryable<>))
							return FUNCTION;

						if (d == typeof(ISingleResult<>))
							return PROCEDURE;

						if (d == null && t == typeof(IMultipleResults) || t.IsSubclassOf(typeof(IMultipleResults)))
							return PROCEDURE;

						if (t != typeof(int))
							return FUNCTION;

						return PROCEDURE;
					}

					return this._Function.IsComposable;
				}

				var a = this._Config.Attributes[MetaSource.IsComposable];
				if (a == null)
				{
					var t = this._Method.ReturnType;
					var d = t.IsGenericType ? t.GetGenericTypeDefinition() : null;
					if (d == typeof(IQueryable<>))
						return FUNCTION;

					if (d == typeof(ISingleResult<>))
						return PROCEDURE;

					if (d == null && t == typeof(IMultipleResults) || t.IsSubclassOf(typeof(IMultipleResults)))
						return PROCEDURE;

					if (t != typeof(int))
						return FUNCTION;

					return PROCEDURE;
				}

				if (string.IsNullOrWhiteSpace(a.Value))
				{
					var t = this._Method.ReturnType;
					var d = t.IsGenericType ? t.GetGenericTypeDefinition() : null;
					if (d == typeof(IQueryable<>))
						return FUNCTION;

					if (d == typeof(ISingleResult<>))
						return PROCEDURE;

					if (d == null && t == typeof(IMultipleResults) || t.IsSubclassOf(typeof(IMultipleResults)))
						return PROCEDURE;

					if (t != typeof(int))
						return FUNCTION;

					return PROCEDURE;
				}

				return bool.Parse(a.Value);
			}
		}

		public override string MappedName
		{
			get
			{
				if (this._Config == null)
				{
					if (this._Function == null || string.IsNullOrWhiteSpace(this._Function.Name))
						return this._Method.Name;

					return this._Function.Name;
				}

				var a = this._Config.Attributes[MetaSource.Name];
				if (a == null)
				{
					if (this._Function == null || string.IsNullOrWhiteSpace(this._Function.Name))
						return this._Method.Name;

					return this._Function.Name;
				}

				if (string.IsNullOrWhiteSpace(a.Value))
				{
					if (this._Function == null || string.IsNullOrWhiteSpace(this._Function.Name))
						return this._Method.Name;

					return this._Function.Name;
				}

				return a.Value;
			}
		}

		public override MethodInfo Method
		{
			get
			{
				return this._Method;
			}
		}

		public override MetaModel Model
		{
			get
			{
				return this._Model;
			}
		}

		public override string Name
		{
			get
			{
				if (this._Config == null)
				{
					if (this._Function == null || string.IsNullOrWhiteSpace(this._Function.Name))
						return this._Method.Name;

					return this._Function.Name;
				}

				var a = this._Config.Attributes[MetaSource.Method];
				if (a == null)
				{
					if (this._Function == null || string.IsNullOrWhiteSpace(this._Function.Name))
						return this._Method.Name;

					return this._Function.Name;
				}

				if (string.IsNullOrWhiteSpace(a.Value))
				{
					if (this._Function == null || string.IsNullOrWhiteSpace(this._Function.Name))
						return this._Method.Name;

					return this._Function.Name;
				}

				return a.Value;
			}
		}

		public override ReadOnlyCollection<MetaParameter> Parameters
		{
			get
			{
				if (this._Parameters != null)
					return new List<MetaParameter>(this._Parameters).AsReadOnly();

				var ps = this._Method.GetParameters();
				var ls = new List<MappingParameter>();
				foreach (var p in ps)
					ls.Add(new MappingParameter(this._Source, this._Model, this, p));
				this._Parameters = ls;
				return new List<MetaParameter>(ls).AsReadOnly();
			}
		}

		public override ReadOnlyCollection<MetaType> ResultRowTypes
		{
			get
			{
				if (this._Results != null)
					return new List<MetaType>(this._Results).AsReadOnly();

				if (this._Config == null)
				{
					var cs = this._Method.GetCustomAttributes(typeof(ResultTypeAttribute), true) as ResultTypeAttribute[];
					if (cs == null || cs.Length == 0)
						return Reflector<MetaType>.EmptyReadOnly;

					var ls = new List<MappingType>();
					foreach (var c in cs)
						ls.Add(new MappingType(this._Source, this._Model, c.Type));
					this._Results = ls;
					return new List<MetaType>(ls).AsReadOnly();
				}

				throw new NotImplementedException();//return new List<MetaType>(this._Results).AsReadOnly();XmlMappingSource
			}
		}

		public override MetaParameter ReturnParameter
		{
			get
			{
				return new MappingParameter(this._Source, this._Model, this, this._Method.ReturnParameter);
			}
		}
	}
}
